package jfox.platform.infrastructure;

import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import org.apache.log4j.Logger;

/**
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 */

public abstract class SuperBO implements BusinessObject{

    protected Logger logger = Logger.getLogger(this.getClass());

    @Resource
    private SessionContext sessionContext;

    protected SessionContext getSessionContext(){
        return sessionContext;
    }

    /**
     * 检查该EntityObject是否被引用
     *
     * @param entityObject entity
     * @throws EntityNotExistedException
     */
    protected void checkEntityReferenced(IRefInspectableEntityObject entityObject) throws EntityReferencedException{
        if(entityObject.isReferenced()){
            throw new EntityReferencedException(entityObject.getClass(), entityObject.getId());
        }
    }

    /**
     * 检查 Version 是否被更新
     * 使用的 namedQuery 必须为通过 ID 获得 Entity 的 query
     * 不使用事务，以避免当前事务无法感知数据更新的问题
     */
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void checkEntityVersion(IVersionableEntityObject entityObject, String namedQuery) throws VersionOverdueException {
        VersionableEntityObject storedEntity = (VersionableEntityObject)getDataAccessObject().getEntityObjectByColumn(namedQuery,"ID",entityObject.getId());
        /*
          1. 如果从从缓存返回的，是同一个entity, 在 entityObject.setVersion 就会抛出异常
          2. 如果已经从Cache销毁，则会从新区的 entity, Version 是最新的数据库值
         */
        if(entityObject != storedEntity && entityObject.getVersion() != storedEntity.getVersion()+1) {
            throw new VersionOverdueException(entityObject.getClass(), entityObject.getId());
        }
    }

    @AroundInvoke
    Object interceptor(InvocationContext invocationContext) throws Exception{
        long now = System.currentTimeMillis();
        logger.info("Enter method: " + invocationContext.getMethod() + ", " + now);
        Object result = invocationContext.proceed();
        logger.info("Returned from method: " + invocationContext.getMethod() + ", consumed " + (System.currentTimeMillis()-now) + "ms.");
        return result;
    }

    public abstract DataAccessObject getDataAccessObject();
}
